shell脚本编程

1.shell脚本是什么

        它是一种脚本语言,并非编程语言

        可以使用一些逻辑判断、循环等语法

        可以自定义子函数

        是系统命令的集合

        shell脚本可以实现自动化运维,大大增加我们的工作效率

2.shell脚本结构以及执行方法

        开头行指定bash路径: #! /bin/bash

        以#开头的行作为解释说明

        脚本的名字以.sh结尾,用于区分这是一个shell脚本

        执行方式有两种:chmod +x 1.sh; ./1.sh 如果没有执行权限可以 bash 1.sh

        bash -x 1.sh 可以查看脚本执行过程

3.学会date命令的用法

  • date +%Y-%m-%d, date +%y-%m-%d 年月日
  • date +%H:%M:%S = date +%T 时间
  • date +%s 时间戳
  • date -d @1434248742
  • date -d “+1day”一天后date -d “-1day”一天前
  • date -d “-1month” 一月前
  • date -d “-1min” 一分钟前
  • date +%w, date +%W 星期

4.shell脚本中的变量

        当脚本中使用某个字符串较频繁并且字符串长度很长时就应该使用变量代替
        使用条件语句时,常常使用变量 if [ $a -gt 1 ]; then … ; fi

        引用某个命令的结果时,用变量替代 n=wc -l 1.txt

        写和用户交互的脚本时,变量也是必不可少的 read -p “Input a number: “ n; echo $n 如果没写这个n,可以直接使用$REPLY

        内置变量 $0, $1, $2… $0表示脚本本身,$1 第一个参数,$2 第二个 …. $#表示参数个数

        数学运算a=1;b=2; c=$(($a+$b))或者$[$a+$b]

5.shell中的逻辑判断

        格式1:

1
if 条件 ; then 语句; fi

        格式2:

1
if 条件; then 语句; else 语句; fi

        格式3:

1
if …; then … ;elif …; then …; else …; fi

        逻辑判断表达式:if [ $a -gt $b ]; if [ $a -lt 5 ]; if [ $b -eq 10 ]等 -gt (>); -lt(<); -ge(>=); -le(<=);-eq(==); -ne(!=) 注意到处都是空格

        可以使用 && || 结合多个条件

6.if 判断文件、目录属性

  • [ -f file ]判断是否是普通文件,且存在
  • [ -d file ] 判断是否是目录,且存在
  • [ -e file ] 判断文件或目录是否存在
  • [ -r file ] 判断文件是否可读
  • [ -w file ] 判断文件是否可写
  • [ -x file ] 判断文件是否可执行
  • [ -s file ] 判断文件大小是否非0
  • [ -c file ] 判断文件是否为字符特殊文件,且存在
  • [ -b file ] 判断文件是否为块特殊文件,且存在
  • [ -t file ] 判断文件是否为描述符(默认为1)指定的设备为终端
  • [ -L file ] 判断制定的是否为符号链接
  • [ “$a” = “$b” ] 判断 $a 和 $b 是否相等
  • [ -n “$var” ] 判断 $var 变量是否有值
  • [ -z ] 判断变量是否存在值
  • [! ] 测试条件的否定符号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[ -a FILE ] 如果 FILE 存在则为真。
[ -b FILE ] 如果 FILE 存在且是一个块特殊文件则为真。
[ -c FILE ] 如果 FILE 存在且是一个字特殊文件则为真。
[ -d FILE ] 如果 FILE 存在且是一个目录则为真。
[ -e FILE ] 如果 FILE 存在则为真。
[ -f FILE ] 如果 FILE 存在且是一个普通文件则为真。
[ -g FILE ] 如果 FILE 存在且已经设置了SGID则为真。
[ -h FILE ] 如果 FILE 存在且是一个符号连接则为真。
[ -k FILE ] 如果 FILE 存在且已经设置了粘制位则为真。
[ -p FILE ] 如果 FILE 存在且是一个名字管道(F如果O)则为真。
[ -r FILE ] 如果 FILE 存在且是可读的则为真。
[ -s FILE ] 如果 FILE 存在且大小不为0则为真。
[ -t FD ] 如果文件描述符 FD 打开且指向一个终端则为真。
[ -u FILE ] 如果 FILE 存在且设置了SUID (set user ID)则为真。
[ -w FILE ] 如果 FILE 如果 FILE 存在且是可写的则为真。
[ -x FILE ] 如果 FILE 存在且是可执行的则为真。
[ -O FILE ] 如果 FILE 存在且属有效用户ID则为真。
[ -G FILE ] 如果 FILE 存在且属有效用户组则为真。
[ -L FILE ] 如果 FILE 存在且是一个符号连接则为真。
[ -N FILE ] 如果 FILE 存在 and has been mod如果ied since it was last read则为真。
[ -S FILE ] 如果 FILE 存在且是一个套接字则为真。
[ FILE1 -nt FILE2 ] 如果 FILE1 has been changed more recently than FILE2, or 如果 FILE1 exists and FILE2 does not则为真。
[ FILE1 -ot FILE2 ] 如果 FILE1 比 FILE2 要老, 或者 FILE2 存在且 FILE1 不存在则为真。
[ FILE1 -ef FILE2 ] 如果 FILE1 和 FILE2 指向相同的设备和节点号则为真。
[ -o OPTIONNAME ] 如果 shell选项 “OPTIONNAME” 开启则为真。
[ -z STRING ] “STRING” 的长度为零则为真。
[ -n STRING ] or [ STRING ] “STRING” 的长度为非零 non-zero则为真。
[ STRING1 == STRING2 ] 如果2个字符串相同。 “=” may be used instead of “==” for strict POSIX compliance则为真。
[ STRING1 != STRING2 ] 如果字符串不相等则为真。
[ STRING1 < STRING2 ] 如果 “STRING1” sorts before “STRING2” lexicographically in the current locale则为真。
[ STRING1 > STRING2 ] 如果 “STRING1” sorts after “STRING2” lexicographically in the current locale则为真。
[ ARG1 OP ARG2 ] “OP” is one of -eq, -ne, -lt, -le, -gt or -ge. These arithmetic binary operators return true if “ARG1” is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to “ARG2”, respectively. “ARG1” and “ARG2” are integers.

7.if 判断一些特殊用法

  • if [ -z $a ] 这个表示当变量a的值为空时会怎么样
  • if grep -q ‘123’ 1.txt; then 表示如果1.txt中含有’123’的行时会怎么样
  • if [ ! -e file ]; then 表示文件不存在时会怎么样
  • if (($a<1)); 1="" then="" …等同于="" if="" [="" $a="" -lt="" ];="" then…="" ]="" 中不能使用<,="">,==,!=,>=,<=这样的符号

8.shell中的case判断

        格式:

1
2
3
4
5
6
7
8
9
10
11
case 变量名 in
value1)
command
;;
value2)
command
;;
*)
commond
;;
esac

        在case程序中,可以在条件中使用|,表示或的意思, 比如

1
2
3
2|3)
command
;;

        当变量为2或者3时,执行该部分命令。

        案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/bin/bash
read -p "Please input a number: " n
if [ -z $n ]
then
echo "Please input a number."
exit 1
fi
n1=`echo $n|sed 's/[-0-9]//g'`
if [ ! -z $n1 ]
then
echo "Please input a number."
exit 1
#elif [ $n -lt 0 ] || [ $n -gt 100 ]
#then
# echo "The number range is 0-100."
# exit 1
fi
if [ $n -lt 60 ]
then
tag=1
elif [ $n -ge 60 ] &amp;&amp; [ $n -lt 80 ]
then
tag=2
elif [ $n -ge 80 ] &amp;&amp; [ $n -lt 90 ]
then
tag=3
elif [ $n -ge 90 ] &amp;&amp; [ $n -le 100 ]
then
tag=4
else
tag=0
fi
case $tag in
1)
echo "不及格"
;;
2)
echo "及格"
;;
3|4)
echo "优秀"
;;
*)
echo "The number range is 0-100."
;;
esac

9.shell脚本中的循环

        for循环语法结构:

1
for 变量名 in 条件; dodone

        案例1:

1
2
3
4
5
6
7
8
#!/bin/bash
sum=0
for i in `seq 1 100`
do
sum=$[$sum+$i]
echo $i
done
echo $sum

        案例2:

1
2
3
4
5
6
7
8
9
#!/bin/bash
cd /etc/
for a in `ls /etc/`
do
if [ -d $a ]
then
ls -d $a
fi
done

        案例3:

1
2
3
4
5
6
#!/bin/bash
n=`wc -l 1.txt |awk '{print $1}'`
for i in `seq 1 $n`
do
sed -n "$i"p 1.txt
done

        while 循环语法结构:

1
while 条件; dodone 死循环用:表示

        案例1:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
while :
do
load=`w|head -1|awk -F 'load average: ' '{print $2}'|cut -d. -f1`
if [ $load -gt 10 ]
then
top|mail -s "load is high: $load" [email]asldkfls@11.com[/email]
fi
sleep 30
done

        案例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
while :
do
read -p "Please input a number: " n
if [ -z $n ]
then
echo "你需要输入东西"
continue
fi
n1=`echo $n|sed 's/[-0-9]//g'`
if [ ! -z $n1 ]
then
echo "你只能输入一个纯数字"
continue
fi
break
done
echo $n

        break直接结束本层循环;

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i == 3 ]
then
break
fi
echo $i
done
echo aaaaaaa

        continue忽略continue之下的代码,直接进行下一次循环

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i == 3 ]
then
continue
fi
echo $i
done
echo $i

        exit 直接退出shell

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i == 3 ]
then
exit
fi
echo $i
done
echo aaaaaaa

10.shell中的函数

        函数就是把一段代码整理到了一个小单元中,并给这个小单元起一个名字,当用到这段代码时直接调用这个小单元的名字即可。

        格式:

1
2
3
function f_name() {
command
}

        函数必须要放在最前面

        案例1:

1
2
3
4
#!/bin/bash
input() {
echo $1
}

        案例2:

1
2
3
4
5
6
#!/bin/bash
sum() {
s=$[$1+$2]
echo $s
}
sum 1 2

案例3:

1
2
3
4
5
6
7
#!/bin/bash
ip() {
ifconfig |grep -A1 "$1 " |tail -1 |awk '{print $2}'|awk -F':' '{print $2}'
}
read -p "Please input the eth name: " e
myip=`ip $e`
echo "$e address is $myip"

        函数里可以export 全局变量

11.shell练习题

        编写shell脚本,计算1-100的和;

1
2
3
4
5
6
#!/bin/bash
sum=0
for((i=1;i<=100;i++));do
sum=$((i+sum))
done
echo $sum

        编写shell脚本,要求输入一个数字,然后计算出从1到输入数字的和,要求,如果输入的数字小于1,则重新输入,直到输入正确的数字为止;

1
2
3
4
5
6
7
8
#!/bin/bash
read -p "Enter a num:" num
sum=0
for i in $(seq $num)
do
sum=$((sum+i))
done
echo "Sum is $sum"
1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
n=0
while [ $i -lt 1 ]
do
read -p "Enter a num:" i
done
sum=0
for n in `seq 1 $i`
do
sum=$[$sum+$n]
done
echo $sum

        编写shell脚本,把/root/目录下的所有目录(只需要一级)拷贝到/tmp/目录下;

1
2
3
4
5
6
7
8
9
#!/bin/bash
cd /root
for i in `ls`
do
if [ -d $i ]
then
cp -r $i /tmp/
fi
done

        编写shell脚本,批量建立用户user_00, user_01, … user_100并且所有用户同属于users组;

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
groupadd users
for i in `seq 0 9`
do
useradd -g users user_0$i
done
for j in `seq 10 100`
do
useradd -g user_$j
done

        编写shell脚本,截取文件test.log中包含关键词 ‘abc’ 的行中的第一列(假设分隔符为 ”:” ),然后把截取的数字排序(假设第一列为数字),然后打印出重复次数超过10次的列;

1
2
3
4
#!/bin/bash
awk -F':' '$0~/abc/ {print $1}' test.log > /tmp/1.txt
sort -n /tmp/1.txt |uniq -c | sort -n > /tmp/2.txt
awk '$1>10 {print $2}' /tmp/2.txt